package com.talent.proxy.mysql.common.packets.s2c;

import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.talent.aio.common.Aio;
import com.talent.aio.common.ChannelContext;
import com.talent.proxy.mysql.common.Capabilities;
import com.talent.proxy.mysql.common.MysqlExt;
import com.talent.proxy.mysql.common.MysqlExt.SessionStatus;
import com.talent.proxy.mysql.common.MysqlPacket;
import com.talent.proxy.mysql.common.MysqlReturn;
import com.talent.proxy.mysql.common.client.ClientConfig;
import com.talent.proxy.mysql.common.client.MysqlClientGroupContext;
import com.talent.proxy.mysql.common.packets.c2s.AuthPacket;
import com.talent.proxy.mysql.common.utils.BufferUtil;
import com.talent.proxy.mysql.common.utils.SecurityUtil;

/**
 * 
 * /**
     * <pre>
     * Bytes                        Name
     *  -----                        ----
     *  1                            protocol_version
     *  n (Null-Terminated String)   server_version
     *  4                            connect_id
     *  8                            scramble_buff
     *  1                            (filler) always 0x00
     *  2                            server_capabilities
     *  1                            server_language
     *  2                            server_status
     *  13                           (filler) always 0x00 ...
     *  13                           rest of scramble_buff (4.1)
     * </pre>
     * @see http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
     */
public class HandshakePacket extends MysqlPacket
{
	private static Logger log = LoggerFactory.getLogger(HandshakePacket.class);
	private static final byte[] FILLER_13 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

	public byte protocolVersion;
	public byte[] serverVersion;
	public String serverVersionStr;
	public long connectId;
	public byte[] seed;
	public int capabilityFlags;
	public byte characterSet;
	public int statusFlags;
	public byte[] restOfScrambleBuff;
	public String lastStr;

	@Override
	public void decodeBody(ByteBuffer buffer, ChannelContext<MysqlExt, MysqlPacket, MysqlReturn> channelContext)
	{

		//		
		//		

		int initposition = buffer.position();

		protocolVersion = BufferUtil.read(buffer);
		serverVersion = BufferUtil.readBytesWithNull(buffer);
		serverVersionStr = new String(serverVersion);

		connectId = BufferUtil.readUB4(buffer);
		seed = BufferUtil.readBytesWithNull(buffer);
		capabilityFlags = BufferUtil.readUB2(buffer);
		characterSet = BufferUtil.read(buffer);
		statusFlags = BufferUtil.readUB2(buffer);

		byte[] ms = BufferUtil.readBytes(buffer, 13);
		restOfScrambleBuff = BufferUtil.readBytesWithNull(buffer);

		int usedlength = buffer.position() - initposition;
		if (usedlength < bodySize)
		{
			lastStr = BufferUtil.readStringWithNull(buffer);
		}

	}

	@Override
	public int bodySize()
	{
		int size = 1;
		size += serverVersion.length;// n
		size += 5;// 1+4
		size += seed.length;// 8
		size += 19;// 1+2+1+2+13
		size += restOfScrambleBuff.length;// 12
		size += 1;// 1
		return size;
	}

	@Override
	public ByteBuffer encode(ChannelContext<MysqlExt, MysqlPacket, MysqlReturn> channelContext)
	{
		int bodySize = bodySize();
		ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + bodySize);
		BufferUtil.writeUB3(buffer, bodySize);
		buffer.put(seqId);
		buffer.put(protocolVersion);
		BufferUtil.writeWithNull(buffer, serverVersion);
		BufferUtil.writeUB4(buffer, connectId);
		BufferUtil.writeWithNull(buffer, seed);
		BufferUtil.writeUB2(buffer, capabilityFlags);
		buffer.put(characterSet);
		BufferUtil.writeUB2(buffer, statusFlags);
		buffer.put(FILLER_13);
		BufferUtil.writeWithNull(buffer, restOfScrambleBuff);
		return buffer;
	}

	@Override
	protected String packetDesc()
	{
		return "MySQL Handshake Packet";
	}

	//	@Override
	//	public MysqlReturn onReceived(ChannelContext<MysqlExt, MysqlPacket, MysqlReturn> channelContext)
	//	{
	//		MysqlExt mysqlExt = channelContext.getSessionContext();
	//
	//		mysqlExt.setHandshakePacket(this);
	//		mysqlExt.setSessionStatus(SessionStatus.CLIENT_WAITING_AUTH_RESP);
	//
	//		MysqlClientGroupContext mysqlClientGroupContext = (MysqlClientGroupContext) channelContext.getGroupContext();
	//		ClientConfig clientConfig = mysqlClientGroupContext.getClientConfig();
	//		String password = clientConfig.getPassword();
	//
	//		AuthPacket authPacket = new AuthPacket();
	//		authPacket.packetId = (byte) (packetId + 1);
	//		authPacket.setCharacterSet(this.serverCharsetIndex);
	//
	//		authPacket.setUser(clientConfig.getUsername());
	//
	//		if (StringUtils.isEmpty(clientConfig.getPassword()))
	//		{
	//			authPacket.setPassword(new byte[] { 0x00 });
	//		} else
	//		{
	//			try
	//			{
	//				authPacket.setPassword(Util.newCrypt(password, new String(this.seed), "utf-8").getBytes());
	//				
	////				 packet.writeString(Util.newCrypt(password, this.seed, this.connection.getPasswordCharacterEncoding()));
	//				
	//			} catch (Exception e)
	//			{
	//				log.error(e.getLocalizedMessage(), e);
	//
	//			}
	//		}
	//
	//		authPacket.setCapabilityFlags(initClientFlags(serverCapabilities));
	//		authPacket.setDatabase(clientConfig.getDb());
	//		//		authPacket.setScrumbleBuff(joinAndCreateScrumbleBuff(this));
	//
	//		Aio.send(channelContext, authPacket);
	//
	//		return null;
	//	}

	/** 
	 * @see com.talent.proxy.mysql.common.MysqlPacket#onReceived(com.talent.aio.common.ChannelContext)
	 * 
	 * @param channelContext
	 * @return
	 * @重写人: tanyaowu
	 * @重写时间: 2017年1月23日 下午1:47:18
	 * 
	 */
	@Override
	public MysqlReturn onReceived(ChannelContext<MysqlExt, MysqlPacket, MysqlReturn> channelContext)
	{
		MysqlExt mysqlExt = channelContext.getSessionContext();

		mysqlExt.setHandshakePacket(this);
		mysqlExt.setSessionStatus(SessionStatus.CLIENT_WAITING_AUTH_RESP);

		MysqlClientGroupContext mysqlClientGroupContext = (MysqlClientGroupContext) channelContext.getGroupContext();
		ClientConfig clientConfig = mysqlClientGroupContext.getClientConfig();

		AuthPacket authPacket = new AuthPacket();
		authPacket.seqId = 1;
		authPacket.setCharacterSet(this.characterSet);

		authPacket.setUser(clientConfig.getUsername());

		if (StringUtils.isEmpty(clientConfig.getPassword()))
		{
			authPacket.setPassword(new byte[] { 0x00 });
		} else
		{
			try
			{
				authPacket.setPassword(encodePwd(clientConfig.getPassword(), this));
			} catch (NoSuchAlgorithmException e)
			{
				log.error(e.getLocalizedMessage(), e);

			}
		}

		authPacket.setCapabilityFlags(initClientFlags(capabilityFlags));
		authPacket.setDatabase(clientConfig.getDb());
		//		authPacket.setScrumbleBuff(joinAndCreateScrumbleBuff(this));

		Aio.send(channelContext, authPacket);

		return null;
	}

	private int initClientFlags(int serverCapabilities)
	{
		int flag = 0;
		flag |= Capabilities.CLIENT_LONG_PASSWORD;
		flag |= Capabilities.CLIENT_FOUND_ROWS;
		flag |= Capabilities.CLIENT_LONG_FLAG;
		flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
		// flag |= Capabilities.CLIENT_NO_SCHEMA;
		//		boolean usingCompress = false;
		//		if (usingCompress)
		//		{
		//			flag |= Capabilities.CLIENT_COMPRESS;
		//		}
		flag |= Capabilities.CLIENT_ODBC;
		flag |= Capabilities.CLIENT_LOCAL_FILES;
		flag |= Capabilities.CLIENT_IGNORE_SPACE;
		flag |= Capabilities.CLIENT_PROTOCOL_41;
		flag |= Capabilities.CLIENT_INTERACTIVE;
		// flag |= Capabilities.CLIENT_SSL;
		flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
		flag |= Capabilities.CLIENT_TRANSACTIONS;
		// flag |= Capabilities.CLIENT_RESERVED;
		flag |= Capabilities.CLIENT_SECURE_CONNECTION;
		
		
		// client extension
		flag |= Capabilities.CLIENT_MULTI_STATEMENTS;
		
		//暂未实现
		flag |= Capabilities.CLIENT_MULTI_RESULTS;

		if ((serverCapabilities & Capabilities.CLIENT_DEPRECATE_EOF) != 0)
		{
			flag |= Capabilities.CLIENT_DEPRECATE_EOF;
		}

		return flag;
	}

	private static byte[] encodePwd(String password, HandshakePacket handshakePacket) throws NoSuchAlgorithmException
	{
		if (password == null || password.length() == 0)
		{
			return null;
		}
		byte[] passwordbytes = password.getBytes();
		int seedlength = handshakePacket.seed.length;
		int seed2length = handshakePacket.restOfScrambleBuff.length;
		byte[] seed = new byte[seedlength + seed2length];
		System.arraycopy(handshakePacket.seed, 0, seed, 0, seedlength);
		System.arraycopy(handshakePacket.restOfScrambleBuff, 0, seed, seedlength, seed2length);
		return SecurityUtil.scramble411(passwordbytes, seed);
	}

}